package com.evolved.automata.android.bots.speechbot;
import java.io.*;
import java.util.*;

public class PointDistribution {

	String serializedFileFullName;

	
	public int averagePower=-1;
	public int powerScale;
	public float angleScale;
	public float speedScale;
	
	
	float powerDistFract=.1F;
	float angleDistFract=.1F;
	float speedDistFract=.1F;
	
	
	LinkedList<Point> pointSet;
	
	public class Point
	{
		int score;
		int motorPower=0;
		float impulseAngle=0;
		float impulseVelocity=0;
		final int minimumScore=0; 
		public float meta;
		
		public Point(int score, float meta, int motorPower, float impulseAngle, float impulseVelocity)
		{
			this.motorPower=motorPower;
			this.impulseAngle=impulseAngle;
			this.impulseVelocity=impulseVelocity;
			this.score=score;
			this.meta=meta;
		}
		
		public String getSerializedForm()
		{
			return String.format("%1$s,%2$s,%3$s,%4$s,%5$s",score,meta, motorPower,impulseAngle,impulseVelocity);
		}
		
		
		
		public void incrementScore()
		{
			score++;
		}
		
		public void decrementScore()
		{
			score--;
			if (score<minimumScore)
				pointSet.remove(this);
		}
		
		public void removePoint()
		{
			pointSet.remove(this);
		}
		
		public void setMeta(float meta)
		{
			this.meta=meta;
		}
		
		public float difference(Point p)
		{
			float delta=0;
//			if (Math.abs(motorPower-p.motorPower)>=powerDistFract*powerScale)
//				return -1.0F;
//			else
//				delta=(motorPower-p.motorPower)*(motorPower-p.motorPower);
			
			if (Math.abs(impulseAngle-p.impulseAngle)>=angleDistFract*angleScale)
				return -1.0F;
			else
				delta+=(impulseAngle-p.impulseAngle)*(impulseAngle-p.impulseAngle);
			
			if (Math.abs(impulseVelocity-p.impulseVelocity)>=speedDistFract*speedScale)
				return -1.0F;
			else
				delta=+(impulseVelocity-p.impulseVelocity)*(impulseVelocity-p.impulseVelocity);
			
			return (float)Math.sqrt(delta);
		}
		
	}
	
	
	public PointDistribution(String serializedFileFullName,int powerScale,float angleScale, float speedScale)
	{
		this.serializedFileFullName=serializedFileFullName;
		this.powerScale=powerScale;
		this.angleScale=angleScale;
		this.speedScale=speedScale;
		pointSet = new LinkedList<PointDistribution.Point>();
	}
	
	public void loadFromFile()
	{
		pointSet = new LinkedList<PointDistribution.Point>();
		BufferedReader reader=null;
		Point newPoint;
		try
		{
			reader = new BufferedReader(new FileReader(serializedFileFullName));
			while ((newPoint = readPoint(reader))!=null)
			{
				pointSet.add(newPoint);
			}
		}
		catch (IOException ie)
		{
			try
			{
				if (reader!=null)
					reader.close();
			}
			catch (Exception e)
			{
				
			}
		}
	}
	
	public void saveToFile()
	{
		BufferedWriter writer=null;
		try
		{
			writer = new BufferedWriter(new FileWriter(serializedFileFullName));
			for (Point p:pointSet)
			{
				writePoint(writer,p);
			}
		}
		catch (IOException ie)
		{
			try
			{
				if (writer!=null)
					writer.close();
			}
			catch (Exception e)
			{
				
			}
		}
	}
	
	public void addPoint(float meta, int motorPower, float impulseAngle, float impulseVelocity)
	{
		pointSet.add(new Point(1,meta,motorPower,impulseAngle,impulseVelocity));
		
	}
	
	public void addPoint(int motorPower, float impulseAngle, float impulseVelocity)
	{
		pointSet.add(new Point(1,0F,motorPower,impulseAngle,impulseVelocity));
		
	}
	
	
	public Point readPoint(BufferedReader reader) throws IOException
	{
		String lineinput=null;
		String[] values=null;
		
		lineinput = reader.readLine();
		
		if ((lineinput==null)||(lineinput.trim().length()==0))
			return null;
		else
		{
			values = StandardTools.splitCommaDelimitedString(lineinput, ',');
			int score = Integer.parseInt(values[0]);
			float meta =Float.parseFloat(values[1]);
			int impulseSize = Integer.parseInt(values[2]);
			float impAngle=Float.parseFloat(values[3]);
			float velAtImpulse=Float.parseFloat(values[4]);
			
			Point p = new Point(score,meta,impulseSize,impAngle,velAtImpulse);
			return p;
			
		}
		
	}
	
	public Point findNearestPoint(float impulseAngle, float impulseVelocity)
	{
		Point temp=new Point(0, 0, 0, impulseAngle, impulseVelocity);
		return findNearestPoint(temp);
	}
	
	public Point findNearestPoint(Point compPoint)
	{
		float average=0;
		int counter=0;
		Point closestPoint=null;
		float smallestDistance=0;
		boolean first=true;
		float currentDistance;
		for (Point basePoint:pointSet)
		{
			average=average*counter/(counter+1)+basePoint.motorPower/(counter+1);
			counter++;
			currentDistance=basePoint.difference(compPoint);
			if ((currentDistance>=0)&&((first)||(currentDistance<=smallestDistance)))
			{
				first=false;
				smallestDistance=currentDistance;
				closestPoint=basePoint;
			}
		}
		if (counter>0)
			averagePower=(int)average;
		else
			averagePower=-1;
		return closestPoint;
	}
	
	public int getAveragePower()
	{
		return averagePower;
	}
	
	public boolean writePoint(BufferedWriter writer, Point p) throws IOException
	{
		
		writer.write(p.getSerializedForm());
		writer.newLine();
		return true;
		
	}
}
